using System;
using System.Collections.Generic;
using System.Reflection;
using LiteNetLib.Utils;

namespace Game
{
	public static class NetworkSerializer
	{
		public interface INetFormatter
		{
			object Deserialize(NetDataReader reader);

			void Serialize(NetDataWriter writer, object value);
		}

		private sealed class IntFormatter : INetFormatter
		{
			public object Deserialize(NetDataReader reader)
			{
				return reader.GetInt();
			}

			public void Serialize(NetDataWriter writer, object value)
			{
				writer.Put((int)value);
			}
		}

		private sealed class FloatFormatter : INetFormatter
		{
			public object Deserialize(NetDataReader reader)
			{
				return reader.GetFloat();
			}

			public void Serialize(NetDataWriter writer, object value)
			{
				writer.Put((float)value);
			}
		}

		private sealed class DoubleFormatter : INetFormatter
		{
			public object Deserialize(NetDataReader reader)
			{
				return reader.GetDouble();
			}

			public void Serialize(NetDataWriter writer, object value)
			{
				writer.Put((double)value);
			}
		}

		private sealed class BoolFormatter : INetFormatter
		{
			public object Deserialize(NetDataReader reader)
			{
				return reader.GetBool();
			}

			public void Serialize(NetDataWriter writer, object value)
			{
				writer.Put((bool)value);
			}
		}

		private sealed class StringFormatter : INetFormatter
		{
			public object Deserialize(NetDataReader reader)
			{
				return reader.GetString();
			}

			public void Serialize(NetDataWriter writer, object value)
			{
				writer.Put((string)value);
			}
		}

		private static Dictionary<Type, INetFormatter> formatters;

		static NetworkSerializer()
		{
			formatters = new Dictionary<Type, INetFormatter>();
			formatters.Add(typeof(int), new IntFormatter());
			formatters.Add(typeof(float), new FloatFormatter());
			formatters.Add(typeof(double), new DoubleFormatter());
			formatters.Add(typeof(bool), new BoolFormatter());
			formatters.Add(typeof(string), new StringFormatter());
		}

		public static void Write<T>(NetDataWriter writer, T value)
		{
			Write(writer, typeof(T), value);
		}

		public static void Write(NetDataWriter writer, Type type, object value)
		{
			INetFormatter value2;
			if (formatters.TryGetValue(type, out value2))
			{
				value2.Serialize(writer, value);
				return;
			}
			if (type.IsValueType)
			{
				Type underlyingType = Nullable.GetUnderlyingType(type);
				if (underlyingType != null)
				{
					if (value != null)
					{
						writer.Put(true);
						Write(writer, underlyingType, Convert.ChangeType(value, underlyingType));
					}
					else
					{
						writer.Put(false);
					}
					return;
				}
				if (type.Assembly == typeof(int).Assembly)
				{
					throw new Exception("invalid call to Serialize<" + type.Name + ">");
				}
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
				foreach (FieldInfo fieldInfo in fields)
				{
					Write(writer, fieldInfo.FieldType, fieldInfo.GetValue(value));
				}
				return;
			}
			if (type.IsArray)
			{
				Array array = value as Array;
				writer.Put(array.Length);
				{
					foreach (object item in array)
					{
						Write(writer, type.GetElementType(), item);
					}
					return;
				}
			}
			throw new Exception("invalid call to Serialize<" + type.Name + ">");
		}

		public static T Read<T>(NetDataReader reader)
		{
			return (T)Read(reader, typeof(T));
		}

		public static object Read(NetDataReader reader, Type type)
		{
			INetFormatter value;
			if (formatters.TryGetValue(type, out value))
			{
				return value.Deserialize(reader);
			}
			if (type.IsValueType)
			{
				Type underlyingType = Nullable.GetUnderlyingType(type);
				if (underlyingType != null)
				{
					if (reader.GetBool())
					{
						return Activator.CreateInstance(type, Read(reader, underlyingType));
					}
					return null;
				}
				if (type.Assembly == typeof(int).Assembly)
				{
					throw new Exception("invalid call to Serialize<" + type.Name);
				}
				object obj = Activator.CreateInstance(type);
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public);
				foreach (FieldInfo fieldInfo in fields)
				{
					fieldInfo.SetValue(obj, Read(reader, fieldInfo.FieldType));
				}
				return obj;
			}
			if (type.IsArray)
			{
				Array array = (Array)Activator.CreateInstance(type, reader.GetInt());
				for (int j = 0; j < array.Length; j++)
				{
					array.SetValue(Read(reader, type.GetElementType()), j);
				}
				return array;
			}
			throw new Exception(string.Format("invalid call to Deserialize<{0}>", type));
		}
	}
}
